import { messages } from "share/common";
import React, { Component, PropTypes } from "react";
import ReactDOM from "react-dom";
import { AdvancedSearch, Table } from "components";
import { Pagination, Icon, Popover, Tooltip } from "antd";
import "styles/components/advanced-table/advanced-table.scss";
import AdvancedSearchTagList from "components/advanced-search/advanced-search-tag-list";
import AdvancedTableFilterDropdown from "./advanced-table-filter-dropdown";
import { hasValue } from "components/advanced-search/utils";

/**
 * 2019-8-1 Wrote by ZaraNengap:
 * 第一次中控风格更改的产物
 * 以前一直想做的页面封装也算是在这里搞定了
 * 组件系统上没什么提升，就是把新风格的组件拼起来了
 * 一方面我在提倡最小组件的状态自治，另一方面又在为了项目的开发效率进行大量的代码封装
 * 又或者这个组件能做到两点不冲突
 * 想找到一个平衡点，需要时间来告诉我它在哪
 *
 * 2019-8-6 Wrote by ZaraNengap:
 * 尝试将一个大组件打散，状态自治，分成各个小组件最后拼装而成
 * 也不是很小，其实硬要分的话还能更小，极小即为react-hooks
 * 好处颇多，比如这里每一个列的筛选项都是复用的AdvancedSearch的，筛选标签列表也能独立出来
 * 当然这么写确实跟本项目传统的写法不一样，维护的时候会混乱于引用了多个文件
 * 但是这种"麻烦"的写法才是未来，大的组件就是应该由多个组件拼接而成
 */
class AdvancedTable extends Component {
  tableContainerRef = null;
  tableRef = null;
  footerRef = null;
  filterTagListRef = null;
  advancedSearchRef = null;
  filterDropdownRefs = {};
  isSafari =
    /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);

  onChangePager = (page) => {
    const { pagination } = this.state;
    if (page !== pagination.current) {
      pagination.current = page;
      this.setState({ pagination, dataSource: [] }, this.getList);
    }
  };

  onChangePagerSize = (current, size) => {
    const { pagination } = this.state;
    if (size !== pagination.pageSize) {
      pagination.pageSize = size;
      pagination.current = 1;
      this.setState({ pagination }, this.getList);
    }
  };

  state = {
    columns: [],
    loading: true,
    dataSource: [],
    scrollY: 0,
    pagination: {
      total: 0,
      current: 1,
      pageSize: 20,
      showSizeChanger: true,
      showQuickJumper: true,
      onShowSizeChange: this.onChangePagerSize,
      onChange: this.onChangePager,
      pageSizeOptions: ["20", "50", "100", "150", "200"],
      size: "small",
      showTotal: (total) =>
        messages("components.key1038", { arg1: total }) /*共{arg1}条*/,
    },
    searchParams: {},
    sourceSearchParams: {},
    filterTags: [],
    dropdownVisible: {},
    pinnedList: [],
    overMinHeight: false,
    scrollWidth: 0,
    expandedAll: false,
    expandedRowKeys: [],
  };

  /**
   * 有关这里为什么用willMount而不是didMount
   * 本来因为Fibber架构的原因，想提前养成习惯，将所有生命周期初始放在didMount里
   * 但是问题是didMount这种生命周期是跟子组件有关
   * 所有子组件先mount完成后，你父组件才调用didMount
   * 这就导致了内部didMount的时候，这里的columns还是空数组，内部取不到真正的值
   * 所以这里换成willMount，能够在子组件didMount之前调用
   * 这个坑可以看到两个东西
   * 1：虽然Fibber架构可能会让willMount执行两次，但是只需要把需要提前远程调用数据的方法放在didMount里就行了，
   *    其他一般操作放到willMount里其实影响不大，所以并不是要强制使用didMount摒弃willMount
   * 2：像这种将props的值赋给state的操作，其实在constructor里就可以直接完成，不需要另起一个生命周期
   *    只有在加载完成后的操作，比如这里的setScrollY可以明确放在加载完成后的操作里
   *    这里虽然写在willMount里，但是写到了setState的第二个函数，其实还是等加载完成才执行
   */
  componentWillMount() {
    const { columns, pinnedList, onRef } = this.props;
    onRef && onRef(this);
    this.setState({ columns, pinnedList }, this.setScrollY);
  }

  setScrollY = () => {
    const { tableScrollMinHeight } = this.props;
    let footerTop = ReactDOM.findDOMNode(this.footerRef).getBoundingClientRect()
      .top;
    let tableNode = ReactDOM.findDOMNode(this.tableContainerRef);
    let tableHeaderTop = 42;
    let tableTop = tableNode.getBoundingClientRect().top;
    let scrollY = footerTop - tableTop - tableHeaderTop;
    let overMinHeight = this.isSafari || scrollY < tableScrollMinHeight;
    this.setState({
      scrollY: overMinHeight ? tableScrollMinHeight : scrollY,
      overMinHeight,
    });
  };

  getList = (reserveExpand) => {
    const { service, extraParams, onGetData, onGetDataError } = this.props;
    const { pagination, searchParams } = this.state;
    const { current, pageSize } = pagination;
    this.setState({ loading: true });
    let callBackPagination = { page: current - 1, pageSize };
    service(current - 1, pageSize, searchParams, extraParams)
      .then((res) => {
        let dataSource = [];
        if (res.data instanceof Array) {
          pagination.total = Number(res.headers["x-total-count"]);
          dataSource = res.data;
        } else if(res.data.totalElements && res.data.totalPages && res.data.content){
          pagination.total = res.data.totalPages;
          dataSource = res.data.content;
        } else {
          pagination.total = res.data.total;
          dataSource = res.data.rows;
        }
        let nextState = {
          loading: false,
          dataSource,
          pagination,
        };
        if (!reserveExpand) {
          nextState = {
            ...nextState,
            expandedAll: false,
            expandedRowKeys: [],
          };
        }
        this.setState(nextState, () => {
          this.resetTableScroll();
          onGetData(
            dataSource,
            { ...callBackPagination, total: pagination.total },
            searchParams
          );
        });
      })
      .catch((e) => {
        this.setState({ loading: false }, () =>
          onGetDataError(e, callBackPagination, searchParams)
        );
      });
  };

  setFilterTags = (values, searchForm, onSet) => {
    this.setState(
      { filterTags: AdvancedSearchTagList.getFilterTags(values, searchForm) },
      () => {
        onSet && onSet();
        this.filterTagListRef && this.filterTagListRef.resetList();
      }
    );
  };

  search = (values, sourceValues, column) => {
    const { dropdownVisible, pagination } = this.state;
    const { searchForm } = this.props;
    if (column) {
      dropdownVisible[column.dataIndex] = false;
      this.advancedSearchRef.props.form.setFieldsValue(sourceValues);
      this.advancedSearchRef.search();
    } else {
      this.setFilterTags(sourceValues, searchForm, () => {
        this.setScrollY();
        pagination.current = 1;
        this.setState(
          {
            pagination,
            dropdownVisible,
            sourceSearchParams: sourceValues,
            searchParams: values,
          },
          () => {
            //刷新ResizeTable的筛选标签状态
            this.tableRef.refreshTableColumns(["filterIcon"]);
            this.getList();
          }
        );
      });
    }
  };

  /**
   * 关于table的滚动条，毕竟scroll不是w3c标准，antd目前没有什么好的解决方法，而且没数据的时候表头滚不了
   * 官方无力：https://github.com/ant-design/ant-design/issues/10490#issuecomment-388687870
   * 大概率 antd-4.0 里会重新设计Table
   * 官方没给方法，所以这里我只能偷鸡，设置margin把下方的滚动条顶了一段，多设置了些内部宽度把右方的滚动条顶没了
   */
  resetTableScroll = () => {
    setTimeout(() => {
      let scrollWidth;
      let tableNode = ReactDOM.findDOMNode(this.tableContainerRef);
      if (this.isSafari) {
        let tableDOM = tableNode.getElementsByClassName("ant-table-fixed")[0];
        let tableFixedDOM = tableNode.getElementsByClassName(
          "ant-table-body"
        )[0];
        let tableRect = tableDOM.getBoundingClientRect();
        let fixedRect = tableFixedDOM.getBoundingClientRect();
        scrollWidth = fixedRect.bottom - tableRect.bottom;
      } else {
        let tableHeaderDOM = tableNode.getElementsByClassName(
          "ant-table-thead"
        )[0];
        let tableBodyDOM = tableNode.getElementsByClassName(
          "ant-table-body"
        )[0];
        let headerRect = tableHeaderDOM.getBoundingClientRect();
        let bodyRect = tableBodyDOM.getBoundingClientRect();
        let tableBodyMarginTop = tableBodyDOM.style.marginTop || 0;
        if (tableBodyMarginTop)
          tableBodyMarginTop = Number(
            tableBodyMarginTop.substr(0, tableBodyMarginTop.length - 2)
          );
        scrollWidth =
          headerRect.top +
          headerRect.height -
          bodyRect.top +
          tableBodyMarginTop;
        if (scrollWidth > 0) {
          tableBodyDOM.style.marginTop = `${scrollWidth}px`;
          tableBodyDOM.style.width = `calc(100% + ${scrollWidth}px)`;
        }
      }
      this.setState({ scrollWidth });
    }, 0);
  };

  renderFilterColumnPopoverTitle = (column) => (
    <div className="advanced-table-filter-column-popover-title">
      {messages("components.key1051", { arg1: column.title }) /*筛选{arg1}*/}
      <Icon
        type="close"
        onClick={this.toState(
          { dropdownVisible: {} },
          () => this.tableRef.refreshTableColumns(["filterIcon"]) //因为在列上，所以需要手动刷新ResizeTable的列属性
        )}
      />
    </div>
  );

  onOpenColumnFilter = (key, searchForm) => {
    this.setState({ dropdownVisible: { [key]: true } }, () => {
      //因为在列上，所以需要手动刷新ResizeTable的列属性
      this.tableRef.refreshTableColumns(["filterIcon"], () => {
        const { sourceSearchParams } = this.state;
        let value = {};
        searchForm.map((field) => {
          value[field.id] = sourceSearchParams[field.id];
        });
        this.filterDropdownRefs[key].props.form.setFieldsValue(value);
      });
    });
  };

  getPopupContainer = () => {
    return ReactDOM.findDOMNode(this);
  };

  getPopoverPlacement = (index) => {
    const { columns } = this.state;
    let left = columns
      .slice(0, index)
      .reduce((column, total) => column.width + total, 0);
    return left < 200 ? "bottomRight" : "bottomLeft";
  };

  getColumns = (columns) => {
    const { dropdownVisible, sourceSearchParams } = this.state;
    const { searchForm } = this.props;
    let columnsSearchForm = {};
    searchForm.map((item) => {
      if (item.dataIndex) {
        if (columnsSearchForm[item.dataIndex])
          columnsSearchForm[item.dataIndex].push(item);
        else columnsSearchForm[item.dataIndex] = [item];
      }
    });
    //关于这里Popover为什么要强控制
    //是因为里面的组件各种各样，而默认控制点击外部DOM就会收起
    //除非你所有组件都用类似getPopupContainer挂载到这里
    //这里为了防止业务组的自定义组件没有关心这个问题
    //所以这里干脆强控制
    return columns.map((column, index) => {
      if (columnsSearchForm[column.dataIndex]) {
        let filtered =
          columnsSearchForm[column.dataIndex].filter((field) =>
            hasValue(field, sourceSearchParams[field.id])
          ).length > 0;
        column.filterIcon = (
          <span
            onClick={() =>
              this.onOpenColumnFilter(
                column.dataIndex,
                columnsSearchForm[column.dataIndex]
              )
            }
          >
            <Popover
              visible={dropdownVisible[column.dataIndex] || false}
              placement={this.getPopoverPlacement(index)}
              overlayClassName="advanced-table-filter-column-popover"
              title={this.renderFilterColumnPopoverTitle(column)}
              content={
                <AdvancedTableFilterDropdown
                  searchForm={columnsSearchForm[column.dataIndex]}
                  column={column}
                  onSearch={(...args) => this.search(...args, column)}
                  onRef={(ref) =>
                    (this.filterDropdownRefs[column.dataIndex] = ref)
                  }
                />
              }
              trigger="click"
              getPopupContainer={this.getPopupContainer}
            >
              <Icon type="filter" className={this.classNames({ filtered })} />
            </Popover>
          </span>
        );
        //给个空的让它显示图标
        column.filterDropdown = <span />;
      }
      return column;
    });
  };

  onChangeColumns = (columns) => {
    this.setState({ columns });
  };


  getFieldsValue = fieldNames => {
    return this.advancedSearchRef.props.form.getFieldsValue(fieldNames);
  };

  getFieldValue = fieldName => {
    return this.advancedSearchRef.props.form.getFieldValue(fieldName);
  };

  setFieldsValue = (values, refresh) => {
    const { searchForm, onSearch, searchNotGetList } = this.props;
    const { pagination } = this.state;
    if(values)
      this.advancedSearchRef.props.form.setFieldsValue(values);
    else
      this.advancedSearchRef.props.form.resetFields();
    //这里直接调用search，search里的表单校验是异步，所以这里Promise化取值
    const setDropDownValues = key => {
      if(values) {
        let dropdownValues =  this.filterDropdownRefs[key].props.form.getFieldsValue();
        let targetValues = {};
        Object.keys(dropdownValues).map(dk => {
          if(values[dk])
            targetValues[dk] = values[dk];
        });
        this.filterDropdownRefs[key].props.form.setFieldsValue(targetValues);
      } else {
        this.filterDropdownRefs[key].props.form.resetFields();
      }
    };
    if(refresh) {
      this.advancedSearchRef.search(true).then(([searchParams, sourceSearchParams]) => {
        let keys = Object.keys(this.filterDropdownRefs);
        let count = 0, keysLength = keys.length;
        pagination.current = 1;
        onSearch && onSearch(pagination, searchParams, sourceSearchParams);
        //dropDown没有渲染的时候是空对象
        if(keysLength === 0) {
          this.setFilterTags(sourceSearchParams, searchForm, () => {
            this.setScrollY();
            this.setState({
              searchParams,
              sourceSearchParams,
              pagination
            }, () => {
              this.tableRef.refreshTableColumns(['filterIcon']);
              refresh && !searchNotGetList && this.getList();
            })
          });
        } else {
          keys.map(key => {
            setDropDownValues(key);
            this.filterDropdownRefs[key].search(true).then(([columnSearchParams, columnSourceSearchParams]) => {
              searchParams = Object.assign({}, searchParams, columnSearchParams);
              sourceSearchParams = Object.assign({}, sourceSearchParams, columnSourceSearchParams);
              count++;
              if(count === keysLength) {
                this.setFilterTags(sourceSearchParams, searchForm, () => {
                  this.setScrollY();
                  this.setState({
                    searchParams,
                    sourceSearchParams,
                    pagination
                  }, () => {
                    this.tableRef.refreshTableColumns(['filterIcon']);
                    this.getList();
                  })
                });
              }
            });
          });
        }
      });
    } else {
      let keys = Object.keys(this.filterDropdownRefs);
      if(keys.length > 0) {
        keys.map(setDropDownValues);
      }
    }
  };

  onCloseTag = (tag) => {
    const { searchForm } = this.props;
    const { filterTags, sourceSearchParams } = this.state;
    //TODO:这里暂时直接设为null，但是考虑到有些自定义组件空值不是null的情况下，是增加nullValue配置项还是让组件处理null值我还没有想好
    sourceSearchParams[tag.id] = null;
    this.setState(
      {
        filterTags: filterTags.filter((item) => item.id !== tag.id),
        sourceSearchParams,
      },
      () => {
        this.advancedSearchRef.props.form.setFieldsValue({ [tag.id]: null });
        let searchItem = searchForm.find((item) => item.id === tag.id);
        if (searchItem && searchItem.dataIndex) {
          this.filterDropdownRefs[
            searchItem.dataIndex
          ].props.form.setFieldsValue({ [tag.id]: null });
        }
        this.advancedSearchRef.search();
      }
    );
  };

  onTagReset = () => {
    const { searchForm } = this.props;
    const { pagination } = this.state;
    this.advancedSearchRef.props.form.resetFields();
    //这里直接调用search，search里的表单校验是异步，所以这里Promise化取值
    this.advancedSearchRef
      .search(true)
      .then(([searchParams, sourceSearchParams]) => {
        let keys = Object.keys(this.filterDropdownRefs);
        let count = 0,
          keysLength = keys.length;
        pagination.current = 1;
        //dropDown没有渲染的时候是空对象
        if (keysLength === 0) {
          this.setFilterTags(sourceSearchParams, searchForm, () => {
            this.setScrollY();
            this.setState(
              {
                searchParams,
                sourceSearchParams,
                pagination,
              },
              this.getList
            );
          });
        } else {
          keys.map((key) => {
            this.filterDropdownRefs[key].props.form.resetFields();
            this.filterDropdownRefs[key]
              .search(true)
              .then(([columnSearchParams, columnSourceSearchParams]) => {
                searchParams = Object.assign(
                  {},
                  searchParams,
                  columnSearchParams
                );
                sourceSearchParams = Object.assign(
                  {},
                  sourceSearchParams,
                  columnSourceSearchParams
                );
                count++;
                if (count === keysLength) {
                  this.setFilterTags(sourceSearchParams, searchForm, () => {
                    this.setScrollY();
                    this.setState(
                      {
                        searchParams,
                        sourceSearchParams,
                        pagination,
                      },
                      this.getList
                    );
                  });
                }
              });
          });
        }
      });
  };

  onChangePinnedList = (pinnedList) => {
    this.setState({ pinnedList }, this.setScrollY);
  };

  onCollapse = () => {
    this.setScrollY();
    this.setState({ dropdownVisible: {} });
  };

  expandAllColumns = () => {
    const { expandedAll, dataSource } = this.state;
    const { rowKey, expandedRowRender } = this.props;
    let expandedRowKeys = [];
    if (!expandedAll)
      dataSource.map((record) => {
        if (expandedRowRender && expandedRowRender(record))
          expandedRowKeys.push(
            typeof rowKey === "string" ? record[rowKey] : rowKey(record)
          );
      });
    this.setState({ expandedAll: !expandedAll, expandedRowKeys });
  };

  onExpand = (expanded, record) => {
    const { rowKey, expandedRowRender } = this.props;
    const { expandedRowKeys, dataSource } = this.state;
    let key = typeof rowKey === "string" ? record[rowKey] : rowKey(record);
    if (expanded) {
      expandedRowKeys.push(key);
    } else {
      expandedRowKeys.delete(key);
    }
    let hasExpandedNumber = dataSource.filter(
      (record) => expandedRowRender && expandedRowRender(record)
    ).length;
    this.setState({
      expandedRowKeys,
      expandedAll: hasExpandedNumber === expandedRowKeys.length,
    });
  };

  render() {
    const {
      searchForm,
      title,
      index,
      tableName,
      children,
      footer,
      expandAll,
      expandedRowRender,
      rowSelection,
      onRow,
      rowKey,
      onChange,
    } = this.props;
    const {
      pagination,
      dataSource,
      loading,
      scrollY,
      filterTags,
      columns,
      pinnedList,
      overMinHeight,
      scrollWidth,
      expandedAll,
      expandedRowKeys,
    } = this.state;
    const transferredConfig = this.getColumns(columns);
    const propsLoading = this.props.loading;
    const indexColumns =
      index &&
      transferredConfig &&
      !transferredConfig.find((c) => c.dataIndex === "_index")
        ? [
            {
              dataIndex: "_index",
              title: messages("common.sequence") /*序号*/,
              width: 72,
              className: "table-row-right-align",
              render: (text, record, index) =>
                index + 1 + pagination.pageSize * (pagination.current - 1),
            },
            ...transferredConfig,
          ]
        : transferredConfig;
    return (
      <div className="advanced-table">
        <div className="advanced-table-scroll">
          <div
            className="advanced-table-scroll-container"
            style={{ marginBottom: overMinHeight ? 48 : 0 }}
          >
            <div className="advanced-table-head-container">
              <AdvancedSearch
                pinnedList={pinnedList}
                onRef={(advancedSearchRef) =>
                  (this.advancedSearchRef = advancedSearchRef)
                }
                searchForm={searchForm}
                title={title}
                onSearch={this.search}
                showFilterTags={false}
                onCollapse={this.onCollapse}
                onChangePinnedList={this.onChangePinnedList}
                tableName={tableName}
              />
              <div className="advanced-table-tag-list-container">
                <AdvancedSearchTagList
                  onRef={(filterTagListRef) =>
                    (this.filterTagListRef = filterTagListRef)
                  }
                  filterTags={filterTags}
                  onSearch={this.getList}
                  onReset={this.onTagReset}
                  onCloseTag={this.onCloseTag}
                />
              </div>
              {children}
            </div>
            <div
              ref={(tableContainerRef) =>
                (this.tableContainerRef = tableContainerRef)
              }
              className={this.classNames([
                "advanced-table-container",
                {
                  "advanced-table-container-expanded": expandedRowRender,
                },
              ])}
            >
              {expandAll && dataSource &&
                dataSource.find(
                  (item) => expandedRowRender && expandedRowRender(item)
                ) && (
                  <Tooltip
                    title={
                      expandedAll
                        ? messages("components.key1075") /*一键收起*/
                        : messages("components.key1076") /*一键展开*/
                    }
                    placement="topLeft"
                  >
                    <div
                      className={this.classNames([
                        "advanced-table-container-all-icon",
                        {
                          "advanced-table-container-all-icon-selection": rowSelection,
                        },
                      ])}
                      onClick={this.expandAllColumns}
                    >
                      {expandedAll ? "-" : "+"}
                    </div>
                  </Tooltip>
                )}
              <Table
                showUtils
                onResize={this.onChangeColumns}
                onSort={this.onChangeColumns}
                onHide={(columns) =>
                  setTimeout(() => this.onChangeColumns(columns), 0)
                }
                onChange={onChange}
                onRow={onRow}
                rowKey={rowKey}
                rowSelection={rowSelection}
                dataSource={dataSource}
                loading={loading || propsLoading}
                scrollY={scrollY}
                rowClassName={(record) =>
                  expandedRowRender && expandedRowRender(record)
                    ? ""
                    : "row-expand-display-none"
                }
                expandedRowRender={expandedRowRender}
                columns={indexColumns}
                pagination={false}
                tableName={tableName}
                expandedRowKeys={expandedRowKeys}
                onExpand={this.onExpand}
                onRef={(tableRef) => (this.tableRef = tableRef)}
              />
            </div>
            <div
              className="advanced-table-footer"
              ref={(footerRef) => (this.footerRef = footerRef)}
              style={overMinHeight ? { bottom: scrollWidth } : {}}
            >
              {rowSelection && (
                <span>
                  {messages("components.key1039") /*已选择*/}
                  <span className="advanced-table-footer-selected-number">
                    {rowSelection.selectedRowKeys.length}
                  </span>
                  {messages("components.key1040") /*条*/}
                </span>
              )}
              <div
                className={this.classNames({
                  "advanced-table-footer-area": !!rowSelection,
                })}
              >
                {footer}
              </div>
              <Pagination {...pagination} />
            </div>
          </div>
        </div>
      </div>
    );
  }

  static propTypes = {
    title: PropTypes.any,
    service: PropTypes.func.isRequired,
    index: PropTypes.bool,
    searchForm: PropTypes.array.isRequired,
    columns: PropTypes.array.isRequired,
    expandedRowRender: PropTypes.func,
    extraParams: PropTypes.object,
    onGetData: PropTypes.func,
    onGetDataError: PropTypes.func,
    pinnedList: PropTypes.array,
    tableName: PropTypes.string,
    tableScrollMinHeight: PropTypes.number,
    expandAll: PropTypes.bool,
  };

  static defaultProps = {
    index: false,
    searchForm: [],
    columns: [],
    onGetData: () => {},
    onGetDataError: () => {},
    pinnedList: [],
    tableScrollMinHeight: 450,
    expandAll: false,
  };
}

AdvancedTable.SimpleTable = (props) => {
  const { show = ["tag-list"], tagListUnderChildren = true } = props;
  const hide = [
    "advanced-search",
    "tag-list",
    "advanced-search-title",
    "advanced-search-form",
    "table-utils",
  ].filter((item) => show.indexOf(item) === -1);
  return (
    <AdvancedTable
      {...props}
      hide={hide}
      className="simple-table"
      tagListUnderChildren={tagListUnderChildren}
    />
  );
};

export default AdvancedTable;
